﻿using System;
using System.Text;
using System.Threading;
using FFmpeg.AutoGen;

namespace FFmpegVideoClip.Lib
{
    [Flags] public enum av_buffersrc_add_frame_flagsNume
    {
        AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT‌ = 1,
        AV_BUFFERSRC_FLAG_PUSH‌ = 4,
        AV_BUFFERSRC_FLAG_KEEP_REF‌ = 8
    }
    public unsafe class Showwavespic
    {

        public static byte[] GetAudioWavSpectrogramImageData(string inputFile, float Volume, int ScreenWidth, int Imageheight, CancellationToken cancellationToken, TimeSpan? startTime, TimeSpan? endTime,AVPixelFormat PixelFormat)
        {
            if (startTime is null) startTime = TimeSpan.Zero;
            if (endTime is null) endTime = TimeSpan.MaxValue;
            int revalue = 0;
            bool EnableVolume = MathF.Abs(Volume) > 0.01f;
            AVFrame* pFrame = null;
            AVPacket* pPacket = null;
            AVFrame* AoutFrame = null;
            AVCodecContext* pCodecContext = null;
            AVFormatContext* pFormatContext = null;
            AVFilterGraph* pFilterGraph = null;
            //SwrContext* swrCtx = null;
            AVFilterContext* pBufferSrcCtx;
            AVFilterContext* pVolumeCtx = null;
            AVFilterContext* pShowwavespicCtx;
            AVFilterContext* pOutputCtx;
            AVFilterContext* format_ctx;
            try
            {
                //// 初始化FFmpeg库
                ffmpeg.av_log_set_level(ffmpeg.AV_LOG_QUIET);
                ////ffmpeg.avformat_network_init();
                //ffmpeg.av_log_set_callback(null);

                // 打开输入文件
                pFormatContext = ffmpeg.avformat_alloc_context();
                if ((revalue = ffmpeg.avformat_open_input(&pFormatContext, inputFile, null, null)) != 0)
                    throw new ShowwavespicException($"无法打开输入文件:{FFmpegHelper.av_strerror(revalue)}");

                // 获取流信息
                if ((revalue = ffmpeg.avformat_find_stream_info(pFormatContext, null)) < 0)
                    throw new ShowwavespicException($"无法获取流信息:{FFmpegHelper.av_strerror(revalue)}");

                // 查找音频流
                int audioStreamIndex = -1;
                for (int i = 0; i < pFormatContext->nb_streams; i++)
                {
                    if (pFormatContext->streams[i]->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                    {
                        audioStreamIndex = i;
                        break;
                    }
                }
                if (audioStreamIndex == -1)
                    throw new ShowwavespicException($"未找到音频流:{FFmpegHelper.av_strerror(revalue)}");
                AVStream* audioStream = pFormatContext->streams[audioStreamIndex];

                // 获取解码器
                AVCodecParameters* pCodecParameters = pFormatContext->streams[audioStreamIndex]->codecpar;
                AVCodec* pCodec = ffmpeg.avcodec_find_decoder(pCodecParameters->codec_id);
                if (pCodec == null)
                    throw new ShowwavespicException($"未找到解码器:{FFmpegHelper.av_strerror(revalue)}");

                // 分配解码器上下文
                pCodecContext = ffmpeg.avcodec_alloc_context3(pCodec);
                ffmpeg.avcodec_parameters_to_context(pCodecContext, pCodecParameters);
                if ((revalue = ffmpeg.avcodec_open2(pCodecContext, pCodec, null)) < 0)
                    throw new ShowwavespicException($"无法打开解码器:{FFmpegHelper.av_strerror(revalue)}");

                AVChannelLayout MonoChannelLayout;
                ffmpeg.av_channel_layout_from_string(&MonoChannelLayout, "mono");

                //AoutFrame->format = (int)AVSampleFormat.AV_SAMPLE_FMT_S16;
                //AoutFrame->sample_rate = pCodecContext->sample_rate;
                //AoutFrame->ch_layout = MonoChannelLayout;
                //AoutFrame->time_base = pFormatContext->streams[audioStreamIndex]->time_base;

                //// 初始化重采样器
                //swrCtx = ffmpeg.swr_alloc();
                //ffmpeg.swr_alloc_set_opts2(
                //&swrCtx,
                //&AoutFrame->ch_layout,               // 输出声道布局
                //(AVSampleFormat)AoutFrame->format,   // 输出格式
                //AoutFrame->sample_rate,              // 输出采样率

                //&pCodecContext->ch_layout,          // 输入声道布局
                //pCodecContext->sample_fmt,          // 输入格式
                //pCodecContext->sample_rate,         // 输入采样率
                //0, null);
                //ffmpeg.swr_init(swrCtx).ThrowExceptionIfError();


                // 创建过滤器图
                pFilterGraph = ffmpeg.avfilter_graph_alloc();


                // 创建buffer源过滤器
                byte* cl_name = stackalloc byte[64];
                ffmpeg.av_channel_layout_describe(&pCodecContext->ch_layout, cl_name, 64);
                string str = Encoding.UTF8.GetString(cl_name, 64);
                string args = $"time_base={pCodecContext->time_base.num}/{pCodecContext->time_base.den}:sample_rate={pCodecContext->sample_rate}:" +
                  $"sample_fmt={ffmpeg.av_get_sample_fmt_name(pCodecContext->sample_fmt)}:channel_layout={str}";

                if ((revalue = ffmpeg.avfilter_graph_create_filter(&pBufferSrcCtx, ffmpeg.avfilter_get_by_name("abuffer"), "src", args, null, pFilterGraph)) < 0)
                    throw new ShowwavespicException($"无法创建buffer源过滤器:参数[{args}]:{FFmpegHelper.av_strerror(revalue)}");

                if (EnableVolume)
                {
                    // volume
                    if ((revalue = ffmpeg.avfilter_graph_create_filter(&pVolumeCtx, ffmpeg.avfilter_get_by_name("volume"), "_volume", $"volume={Volume}dB", null, pFilterGraph)) < 0)
                        throw new ShowwavespicException($"无法创建Volume过滤器:{FFmpegHelper.av_strerror(revalue)}");
                }

                // showwavespic
                if ((revalue = ffmpeg.avfilter_graph_create_filter(&pShowwavespicCtx, ffmpeg.avfilter_get_by_name("showwavespic"), "_showwavespic", $"s={ScreenWidth}x{Imageheight}:colors=white", null, pFilterGraph)) < 0)
                    throw new ShowwavespicException($"无法创建Showwavespic过滤器:{FFmpegHelper.av_strerror(revalue)}");

                // format
                if ((revalue = ffmpeg.avfilter_graph_create_filter(&format_ctx, ffmpeg.avfilter_get_by_name("format"), "format", "pix_fmts=bgra", null, pFilterGraph)) < 0)
                    throw new ShowwavespicException($"无法创建 format 过滤器:{FFmpegHelper.av_strerror(revalue)}");

                // 1.创建 buffersink 作为输出终点
                if ((revalue = ffmpeg.avfilter_graph_create_filter(&pOutputCtx, ffmpeg.avfilter_get_by_name("buffersink"), "output", null, null, pFilterGraph)) < 0)
                    throw new ShowwavespicException($"无法创建 buffersink 过滤器:{FFmpegHelper.av_strerror(revalue)}");


                if (EnableVolume)
                {
                    // 2. 先连接 src -> Volume
                    if ((revalue = ffmpeg.avfilter_link(pBufferSrcCtx, 0, pVolumeCtx, 0)) < 0)
                        throw new ShowwavespicException($"无法连接 src -> volume:{FFmpegHelper.av_strerror(revalue)}");


                    // 3. 再连接 Volume -> showwavespic
                    if ((revalue = ffmpeg.avfilter_link(pVolumeCtx, 0, pShowwavespicCtx, 0)) < 0)
                        throw new ShowwavespicException($"无法连接 src -> volume:{FFmpegHelper.av_strerror(revalue)}");


                    // 4. 再连接 showwavespic -> format
                    if ((revalue = ffmpeg.avfilter_link(pShowwavespicCtx, 0, format_ctx, 0)) < 0)
                        throw new ShowwavespicException($"无法连接 showwavespic -> abuffersink:{FFmpegHelper.av_strerror(revalue)}");

                }
                else
                {
                    // 2. 先连接 src -> Showwavespic
                    if ((revalue = ffmpeg.avfilter_link(pBufferSrcCtx, 0, pShowwavespicCtx, 0)) < 0)
                        throw new ShowwavespicException($"无法连接 src -> Showwavespic:{FFmpegHelper.av_strerror(revalue)}");
                    // 3. 再连接 showwavespic -> format
                    if ((revalue = ffmpeg.avfilter_link(pShowwavespicCtx, 0, format_ctx, 0)) < 0)
                        throw new ShowwavespicException($"无法连接 showwavespic -> abuffersink:{FFmpegHelper.av_strerror(revalue)}");


                }

                // 最后链接 format -> buffersink
                if ((revalue = ffmpeg.avfilter_link(format_ctx, 0, pOutputCtx, 0)) < 0)
                    throw new ShowwavespicException($"无法连接 showwavespic -> abuffersink:{FFmpegHelper.av_strerror(revalue)}");

                // 配置过滤器图
                if ((revalue = ffmpeg.avfilter_graph_config(pFilterGraph, null)) < 0)
                    throw new ShowwavespicException($"配置过滤器图_avfilter_graph_config失败:{FFmpegHelper.av_strerror(revalue)}");


                // 读取并处理数据包
                pPacket = ffmpeg.av_packet_alloc();
                pFrame = ffmpeg.av_frame_alloc();

                // 跳转到指定开始时间

                long StartTimestamp = (long)(startTime.Value.TotalSeconds * audioStream->time_base.den / audioStream->time_base.num);
                if ((revalue = ffmpeg.av_seek_frame(pFormatContext, audioStreamIndex, StartTimestamp, ffmpeg.AVSEEK_FLAG_BACKWARD)) < 0)
                    throw new ShowwavespicException($"无法跳转到指定时间:{FFmpegHelper.av_strerror(revalue)}");

                if (cancellationToken.IsCancellationRequested)
                    return [];

                AoutFrame = ffmpeg.av_frame_alloc();
                while ((revalue = ffmpeg.av_read_frame(pFormatContext, pPacket)) >= 0)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        ffmpeg.av_packet_unref(pPacket);
                        return [];
                    }
                    if (pPacket->stream_index == audioStreamIndex)
                    {
                        // 解码数据包
                        if ((revalue = ffmpeg.avcodec_send_packet(pCodecContext, pPacket)) < 0)
                        {
                            ffmpeg.av_packet_unref(pPacket);
                            continue;
                        }

                        while ((revalue = ffmpeg.avcodec_receive_frame(pCodecContext, pFrame)) == 0)
                        {
                            if (cancellationToken.IsCancellationRequested)
                            {
                                ffmpeg.av_packet_unref(pPacket);
                                ffmpeg.av_frame_unref(pFrame);
                                return [];
                            }

                            // 检查是否超到达开始时间
                            double pts = pFrame->pts * ffmpeg.av_q2d(audioStream->time_base);
                            if (pts < startTime.Value.TotalSeconds)
                            {
                                ffmpeg.av_frame_unref(pFrame);
                                continue;
                            }
                            // 检查是否超过结束时间  
                            if (pts > endTime.Value.TotalSeconds)
                                ffmpeg.av_buffersrc_add_frame_flags(pBufferSrcCtx, null, (int)av_buffersrc_add_frame_flagsNume.AV_BUFFERSRC_FLAG_PUSH);

                            // 将帧发送到过滤器图
                            else if ((revalue = ffmpeg.av_buffersrc_add_frame_flags(pBufferSrcCtx, pFrame, (int)av_buffersrc_add_frame_flagsNume.AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
                            {
                                ffmpeg.av_packet_unref(pPacket);
                                ffmpeg.av_frame_unref(pFrame);
                                throw new ShowwavespicException($"无法向过滤器图添加帧:{FFmpegHelper.av_strerror(revalue)}");
                            }
                            ffmpeg.av_frame_unref(pFrame);
                            
                            //else if ((revalue = ffmpeg.swr_convert_frame(swrCtx, AoutFrame, pFrame)) == 0)
                            //{
                            //    if (cancellationToken.IsCancellationRequested)
                            //        return [];
                            //    //ffmpeg.av_frame_copy(To_outFrame,AoutFrame);
                            //    //To_outFrame->best_effort_timestamp = pFrame->best_effort_timestamp;
                            //    //To_outFrame->pts = pFrame->pts;
                            //    //To_outFrame->pkt_dts = pFrame->pkt_dts;
                            //    //To_outFrame->sample_aspect_ratio = pFrame->sample_aspect_ratio;

                            //    // 将帧发送到过滤器图
                            //    if ((revalue = ffmpeg.av_buffersrc_add_frame_flags(pBufferSrcCtx, AoutFrame, (int)av_buffersrc_add_frame_flagsNume.AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
                            //        throw new ShowwavespicException($"无法向过滤器图添加帧:{FFmpegHelper.av_strerror(revalue)}");
                            //    //if ((revalue = ffmpeg.av_buffersrc_add_frame(pBufferSrcCtx, AoutFrame)) < 0)
                            //    //	throw new ShowwavespicException($"无法向过滤器图添加帧:{FFmpegHelper.av_strerror(revalue)}");
                            //}
                            //else throw new ShowwavespicException($"音频重采样异常:{FFmpegHelper.av_strerror(revalue)}");

                            // 从过滤器图获取处理后的帧
                            if ((revalue = ffmpeg.av_buffersink_get_frame(pOutputCtx, AoutFrame)) == 0)
                            {
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    ffmpeg.av_packet_unref(pPacket);
                                    ffmpeg.av_frame_unref(pFrame);
                                    ffmpeg.av_frame_unref(AoutFrame);
                                    return [];
                                }

                                byte[] bgraArray = new byte[AoutFrame->width * AoutFrame->height * 4];
                                fixed (byte* destPtr = bgraArray)
                                {
                                    Buffer.MemoryCopy(
                                        AoutFrame->data[0],     // 源指针
                                        destPtr,                // 目标指针
                                        bgraArray.Length,       // 目标缓冲区大小
                                        bgraArray.Length        // 拷贝字节数
                                    );
                                }
                                ffmpeg.av_packet_unref(pPacket);
                                ffmpeg.av_frame_unref(pFrame);
                                ffmpeg.av_frame_unref(AoutFrame);
                                return bgraArray;
                            }
                            ffmpeg.av_frame_unref(AoutFrame);
                            if (revalue == ffmpeg.AVERROR(ffmpeg.EAGAIN)) { }
                            else
                            {
                                ffmpeg.av_packet_unref(pPacket);
                                ffmpeg.av_frame_unref(pFrame);
                                throw new ShowwavespicException($"av_buffersink_get_frame获取帧失败:{FFmpegHelper.av_strerror(revalue)}");
                            }
                            ffmpeg.av_frame_unref(pFrame);
                        }
                        ffmpeg.av_frame_unref(pFrame);
                    }
                    ffmpeg.av_packet_unref(pPacket);
                }
                ffmpeg.av_packet_unref(pPacket);
                if (cancellationToken.IsCancellationRequested)
                    return [];
                
                if (revalue == ffmpeg.AVERROR_EOF)
                {
                    if ((revalue = ffmpeg.av_buffersrc_add_frame_flags(pBufferSrcCtx, null, (int)av_buffersrc_add_frame_flagsNume.AV_BUFFERSRC_FLAG_PUSH)) < 0)
                        throw new ShowwavespicException($"无法向过滤器图添加帧:{FFmpegHelper.av_strerror(revalue)}");

                    if ((revalue = ffmpeg.av_buffersink_get_frame(pOutputCtx, AoutFrame)) == 0)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            ffmpeg.av_frame_unref(AoutFrame);
                            return [];
                        }

                        byte[] bgraArray = new byte[AoutFrame->width * AoutFrame->height * 4];
                        fixed (byte* destPtr = bgraArray)
                        {
                            Buffer.MemoryCopy(
                                AoutFrame->data[0], // 源指针
                                destPtr,                // 目标指针
                                bgraArray.Length,       // 目标缓冲区大小
                                bgraArray.Length        // 拷贝字节数
                            );
                        }
                        ffmpeg.av_frame_unref(AoutFrame);
                        return bgraArray;
                    }
                    throw new ShowwavespicException($"av_buffersink_get_frame获取帧失败:{FFmpegHelper.av_strerror(revalue)}");
                }
                throw new ShowwavespicException($"av_read_frame失败:{FFmpegHelper.av_strerror(revalue)}");

            }
            finally
            {
                // 清理资源
                if (pFrame != null)
                    ffmpeg.av_frame_free(&pFrame);

                if (AoutFrame != null)
                    ffmpeg.av_frame_free(&AoutFrame);

                if (pPacket != null)
                    ffmpeg.av_packet_free(&pPacket);

                if (pFilterGraph != null)
                    ffmpeg.avfilter_graph_free(&pFilterGraph);


                //if (swrCtx != null)
                //    ffmpeg.swr_free(&swrCtx);

                if (pCodecContext != null)
                    ffmpeg.avcodec_free_context(&pCodecContext);

                if (pFormatContext != null)
                    ffmpeg.avformat_close_input(&pFormatContext);
            }
        }


        [Serializable]
        public class ShowwavespicException : Exception
        {
            public ShowwavespicException() { }
            public ShowwavespicException(string message) : base(message) { }
            public ShowwavespicException(string message, Exception inner) : base(message, inner) { }
        }

    }

}